
#include <BScript>
#include "member_BScript.h"
#include "member_BSource.h"
#include "member_BClass.h"

using namespace BWE;

#define member					(*(member_BScript*)_ptr)
#define member_allocate()		_ptr = new member_BScript(this)
#define member_release()		delete (member_BScript*)_ptr

BScript::BScript()
{
	member_allocate();
}
BScript::~BScript()
{
	member_release();
}

bool BScript::regist(const char* type, BClass* cls)
{
	if (cls == 0)
		return false;
	if (member.presets.classMap.contain(type))
		return false;
	auto it = member.presets.classMap.insert(type, cls);
	return true;
}
bool BScript::regist(const BString& type, BClass* cls)
{
	if (cls == 0)
		return false;
	if (member.presets.classMap.contain(type))
		return false;
	auto it = member.presets.classMap.insert(type, cls);
	return true;
}
bool BScript::regist(const char* declare, CFunc cfunc)
{
	BString type;
	BString name;
	BStringList types;
	if (!AnalyseDeclare(declare, type, name, types))
		return false;
	if (member.functionMap.contain(name))
		return false;
	Function* function = new Function();
	function->type = type;
	function->cfunc = cfunc;
	function->params.resize(types.size());
	for (int i = 0; i < types.size(); i++)
	{
		BString& word = types[i];
		function->params[i].setType(word);
	}
	member.presets.functionMap[name] = function;
	return true;
}
bool BScript::regist(const BString& declare, CFunc cfunc)
{
	BString type;
	BString name;
	BStringList types;
	if (!AnalyseDeclare(declare, type, name, types))
		return false;
	if (member.functionMap.contain(name))
		return false;
	Function* function = new Function();
	function->type = type;
	function->cfunc = cfunc;
	function->params.resize(types.size());
	for (int i = 0; i < types.size(); i++)
	{
		BString& word = types[i];
		function->params[i].setType(word);
	}
	member.presets.functionMap[name] = function;
	return true;
}

bool BScript::insertSource(BSource* source)
{
	if (!source)
		return false;
	if (source_member(source)->script == this)
		return true;
	member.sources.append(source);
	if (BScript* script = source_member(source)->script)
		script->removeSource(source);
	source_member(source)->script = this;
	return true;
}
bool BScript::removeSource(const BSource* source)
{
	if (!source)
		return false;
	if (source_member(source)->script == this)
	{
		source_member(source)->script = 0;
		member.sources.remove(source);
		return true;
	}
	return false;
}
bool BScript::containSource(const BSource* source)
{
	return member.sources.contain(source);
}
bool BScript::clearSources()
{
	member.sources.clear();
	for (int i = 0; i < member.sequence.size(); i++)
	{
		BExp* exp = member.sequence[i];
		exp->info.clear();
	}
	return true;
}

int BScript::sourceCount() const
{
	return member.sources.size();
}
BSource* BScript::source(int index)
{
	return member.sources(index);
}
const BSource* BScript::source(int index) const
{
	return member.sources(index);
}
BSource* BScript::source(const BString& name)
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.sources.size(); i++)
	{
		BSource* source = member.sources[i];
		if (source->name() == name)
			return source;
	}
	return 0;
}
const BSource* BScript::source(const BString& name) const
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.sources.size(); i++)
	{
		const BSource* source = member.sources[i];
		if (source->name() == name)
			return source;
	}
	return 0;
}

void BScript::setDebugging(bool debugging)
{
	AutoLock lock(this);
	member.debugging = debugging;
	if (member.pausedHandle)
	{
		member.debugResume();
	}
}
bool BScript::debugging() const
{
	AutoLock lock(this);
	return member.debugging;
}

bool BScript::insertBreakpoint(const BString& breakpoint)
{
	member.breakpointSet.insert(breakpoint);
	return true;
}
bool BScript::removeBreakpoint(const BString& breakpoint)
{
	return member.breakpointSet.remove(breakpoint);
}
bool BScript::checkBreakpoint(const BString& breakpoint)
{
	if (member.sequence.empty())
		return false;
	if (breakpoint.empty())
		return false;
	if (breakpoint.contain(':'))
	{
		int line = breakpoint.substr(breakpoint.find(':') + 1, breakpoint.size()).toInt();
		for (int i = 0; i < member.sequence.size(); i++)
		{
			BExp* exp = member.sequence[i];
			if (exp->info == breakpoint)
			{
				return true;
			}
		}
		return false;
	}
	int number = breakpoint.toInt();
	return member.sequence.check(number);
}
bool BScript::clearBreakpoints()
{
	member.breakpointSet.clear();
	return true;
}

int BScript::breakpointCount() const
{
	return member.breakpointSet.size();
}
const BString& BScript::breakpoint(int index) const
{
	return member.breakpointSet[index];
}

bool BScript::compile()
{
	if (member.running)
	{
		BString text = "clear script which is running.";
		this->emit(Signal_Error, text);
		return false;
	}

	if (member.sources.empty())
		return true;

	member.error = false;
	member.message.clear();

	int failedCount = 0;
	int sucessCount = 0;
	for (int i = 0; i < member.sources.size(); i++)
	{
		BSource* source = member.sources[i];
		if (source_member(source)->dirty)
		{
			this->emit(Signal_Message, source->name());
			if (source_member(source)->compile())
				sucessCount++;
			else
				failedCount++;
		}
	}
	if (failedCount > 0)
		return false;

	if (sucessCount < 1)
		return true;

	member.current = -1;
	member.global = -1;
	member.stack.clear();
	member.sequence.clear();
	member.includes.clear();
	member.declares.clear();
	member.classMap.clear();
	member.functionMap.clear();
	member.combine();
	if (member.error)
		return false;

	member.flatten();
	if (member.error)
		return false;

	member.link();
	if (member.error)
		return false;

	BString info;
	for (int i = 0; i < member.sequence.size(); i++)
	{
		BExp* exp = member.sequence[i];
		if (exp->info.empty())
			continue;
		int pos = exp->info.find('(');
		if (pos == exp->info.size())
			continue;
		exp->info.remove(pos, -1);
		if (exp->info == info)
			exp->info.clear();
		else
			info = exp->info;
	}
	member.current = -1;
	member.global = -1;
	member.stack.reset();
	member.stack.reserve(200);
	this->emit(Signal_Compiled);
	return true;
}
bool BScript::reset()
{
	if (this->trylock())
	{
		if (!member.running)
		{
			member.running = false;
			member.current = -1;
			member.global = -1;
			member.stack.reset();
			member.stack.reserve(200);
			this->unlock();
			return true;
		}
		this->unlock();
	}
	return false;
}
bool BScript::pcall(const BString& name, int argc, BVariant* argv)
{
	if (member.sequence.empty())
		return false;
	member.error = false;
	member.running = true;
	if (member.current < 0)
	{
		member.current = 0;
		member.global = 0;
		member.stack.reset();
		member.stack.reserve(200);
		while (member.current < member.sequence.size())
		{
			BExp* exp = member.sequence[member.current];
			member.current = member.perform(exp);
			if (member.error)
			{
				member.running = false;
				return false;
			}
			if (member.debugging)
			{
				member.debug(exp);
				if (member.command == "stop")
				{
					member.command.clear();
					member.running = false;
					return true;
				}
			}
		}
		member.global = member.stack.size();
		this->emit(Signal_Inited);
	}
	if (name.empty())
	{
		member.running = false;
		return true;
	}
	BString funcName = name;
	funcName << '(';
	for (int i = 1; i < argc; i++)
	{
		funcName << argv[i].type() << ',';
	}
	funcName -= ',';
	funcName << ')';
	Function* function = member.findFunction(funcName);
	if (function == 0)
	{
		member.running = false;
		funcName.prepend("function ");
		funcName.append(" is unfound.");
		this->emit(Signal_Error, funcName);
		return false;
	}
	if (function->params.size() != argc - 1)
	{
		member.running = false;
		funcName.prepend("function ");
		funcName.append(" params count don't match.");
		this->emit(Signal_Error, funcName);
		return false;
	}
	for (int i = 0; i < function->params.size(); i++)
	{
		if (function->params[i].type() != argv[i + 1].type())
		{
			funcName.prepend("function ");
			funcName.append(" params don't match.");
			this->emit(Signal_Error, funcName);
			member.running = false;
			return false;
		}
	}
	if (function->sfunc)
	{
		int size = member.stack.size();
		BVariant& res = member.stack.push();
		res.setType(function->type);
		res.setName("retval");
		for (int i = 0; i < function->params.size(); i++)
		{
			BVariant& var = member.stack.push();
			BVariant& arg = argv[i + 1];
			var.setType(arg.type());
			var.setValue(arg.value());
		}
		CallInfo* cinfo = new CallInfo();
		cinfo->type = function->type;
		cinfo->pos = member.sequence.size();
		cinfo->top = member.stack.size() - function->params.size();
		BVariant& var = member.stack.push();
		var.setType("call");
		var.setName(function->sfunc->name);
		var.setValue(cinfo);
		member.current = function->line;
		while (member.current < member.sequence.size())
		{
			BExp* exp = member.sequence[member.current];
			member.current = member.perform(exp);
			if (member.error)
			{
				member.running = false;
				return false;
			}
			if (member.debugging)
			{
				member.debug(exp);
				if (member.command == "stop")
				{
					member.command.clear();
					member.stack.back(size);
					member.running = false;
					return true;
				}
			}
		}
		if (res.type().size())
		{
			argv[0] = res;
		}
		member.stack.back(size);
		member.running = false;
		return true;
	}
	int size = member.stack.size();
	BVariant& res = member.stack.push();
	res.setType(function->type);
	res.setName("retval");
	for (int i = 0; i < function->params.size(); i++)
	{
		BVariant& param = member.stack.push();
		param.setType(function->params[i].type());
		param.setName(function->params[i].name());
		param.setValue(argv[i].value());
	}
	function->cfunc(argc, argv);
	if (res.type().size())
	{
		argv[0] = res;
	}
	member.stack.back(size);
	member.running = false;
	return true;
}
bool BScript::debug(const BString& command)
{
	if (this->lock())
	{
		if (member.pausedHandle)
		{
			member.debugResume();
		}
		member.breaked = false;
		member.command = command;
		this->unlock();
		return true;
	}
	return false;
}
bool BScript::error() const
{
	bool error = false;
	if (this->lock())
	{
		error = member.error;
		this->unlock();
	}
	return error;
}
bool BScript::inited() const
{
	bool inited = false;
	if (this->lock())
	{
		inited = member.current >= 0;
		this->unlock();
	}
	return inited;
}
bool BScript::breaked() const
{
	bool breaked = false;
	if (this->lock())
	{
		breaked = member.breaked;
		this->unlock();
	}
	return breaked;
}
bool BScript::running() const
{
	bool running = false;
	if (this->lock())
	{
		running = member.running;
		this->unlock();
	}
	return running;
}
bool BScript::clear()
{
	if (member.running)
	{
		BString message("clear script which is running.");
		this->emit(Signal_Error, message);
		return false;
	}
	member.sequence.clear();
	member.includes.clear();
	member.declares.clear();
	member.classMap.clear();
	member.functionMap.clear();
	return true;
}

BClass* BScript::findClass(const BString& type)
{
	if (BClass* cls = member.basicClassMap(type))
		return cls;
	if (BClass* cls = member.presets.classMap(type))
		return cls;
	if (BClass* cls = member.classMap(type))
		return cls;
	return 0;
}
const BClass* BScript::findClass(const BString& type) const
{
	if (const BClass* cls = member.basicClassMap(type))
		return cls;
	if (const BClass* cls = member.presets.classMap(type))
		return cls;
	if (const BClass* cls = member.classMap(type))
		return cls;
	return 0;
}

BVariant* BScript::findVariant(const BString& declare)
{
	if (member.global >= 0)
	{
		if (declare.contain('.'))
		{
			BString name;
			int pos = declare.word(name, 0);
			BVariant* ptr = 0;
			for (int i = 0; i < member.global; i++)
			{
				BVariant& var = member.stack[i];
				if (var.name() == name)
				{
					ptr = &var;
					break;
				}
			}
			while (ptr && pos < declare.size())
			{
				BClass* cls = ptr->value();
				if (!cls)
					return 0;
				pos = declare.word(name, pos + 1);
				auto it = class_member(cls)->variantMap.find(name);
				ptr = &it.value();
			}
			return ptr;
		}
		else
		{
			for (int i = 0; i < member.global; i++)
			{
				BVariant& var = member.stack[i];
				if (var.name() == declare)
				{
					return &var;
				}
			}
		}
	}
	return 0;
}
const BVariant* BScript::findVariant(const BString& declare) const
{
	if (member.current >= 0)
	{
		if (declare.contain('.'))
		{
			BString name;
			int pos = declare.word(name, 0);
			BVariant* ptr = 0;
			for (int i = 0; i < member.global; i++)
			{
				BVariant& var = member.stack[i];
				if (var.name() == name)
				{
					ptr = &var;
					break;
				}
			}
			while (ptr && pos < declare.size())
			{
				BClass* cls = ptr->value();
				if (!cls)
					return 0;
				pos = declare.word(name, pos + 1);
				auto it = class_member(cls)->variantMap.find(name);
				ptr = &it.value();
			}
			return ptr;
		}
		else
		{
			for (int i = 0; i < member.global; i++)
			{
				BVariant& var = member.stack[i];
				if (var.name() == declare)
				{
					return &var;
				}
			}
		}
	}
	return 0;
}

bool BScript::setValue(const BString& declare, const BValue& value)
{
	if (member.global >= 0)
	{
		if (BVariant* ptr = findVariant(declare))
		{
			if (BClass* cls = ptr->value())
				return false;
			ptr->setValue(value);
			return true;
		}
	}
	return false;
}
const BValue& BScript::value(const BString& declare)
{
	if (member.global >= 0)
	{
		if (BVariant* ptr = findVariant(declare))
		{
			return ptr->value();
		}
	}
	return member.stack.def().value();
}

bool BScript::setSequence(const BExpHolderArray& sequence)
{
	if (&member.sequence == &sequence)
		return true;
	member.sequence = sequence;
	BClass* cls = 0;
	for (int i = 0; i < sequence.size(); i++)
	{
		BExp* exp = sequence[i];
		if (exp->syntax == Syntax_Include)
		{
			member.include(exp->name);
			continue;
		}
		if (exp->syntax == Syntax_Class)
		{
			cls = new BClass();
			for (int i = 0; i < exp->params.size(); i++)
			{
				BExp* param = exp->params[i];
				if (param->syntax == Syntax_Decl)
				{
					BVariant& var = class_member(cls)->variantMap[param->name];
					var.setType(param->type);
					var.setName(param->name);
					continue;
				}
				if (param->syntax == Syntax_Method)
				{
					Method* method = new Method();
					method->virt = true;
					method->value = param->value;
					class_member(cls)->methodMap[param->name] = method;
					continue;
				}
				param->value = this->findClass(param->type);
				if (param->value.empty())
				{
					member.errorMessage(param) << "class " << exp->name << " inherits failed from " << param->type;
					this->emit(Signal_Message, member.message);
					continue;
				}
				class_member(cls)->inherits.append(param);
			}
			member.classMap[exp->name] = cls;
			continue;
		}
		if (exp->syntax == Syntax_Method)
		{
			if (!cls)
			{
				member.errorMessage(exp) << "regist method " << exp->name << "failed, out of class.";
				this->emit(Signal_Message, member.message);
				continue;
			}
			Method* method = new Method();
			method->type = exp->type;
			method->value = exp;
			method->line = i;
			for (int i = 0; i < exp->params.size(); i++)
			{
				BExp* param = exp->params[i];
				BVariant& var = method->params.append();
				var.setType(param->type);
				var.setName(param->name);
			}
			class_member(cls)->methodMap[exp->name] = method;
			continue;
		}
		if (exp->syntax == Syntax_Function)
		{
			cls = 0;
			Function* function = new Function();
			function->type = exp->type;
			function->sfunc = exp;
			function->line = i;
			for (int i = 0; i < exp->params.size(); i++)
			{
				BExp* param = exp->params[i];
				BVariant& var = function->params.append();
				var.setType(param->type);
				var.setName(param->name);
			}
			member.functionMap[exp->name] = function;
			continue;
		}
	}
	member.link();
	if (member.error)
		return false;
	return true;
}
const BExpHolderArray& BScript::sequence() const
{
	return member.sequence;
}

bool BScript::input(const BString& text, const BValue& value)
{
	if (text == "sequence")
	{
		const BExpHolderArray& sequence = value;
		setSequence(sequence);
	}
	if (text == "classes")
	{
		const BMap<BString, BClassHolder>& classMap = value;
		member.classMap = classMap;
		return true;
	}
	if (text == "functions")
	{
		const BMap<BString, FunctionHolder>& functionMap = value;
		member.functionMap = functionMap;
		return true;
	}
	if (text == "classes-registed")
	{
		const BMap<BString, BClassHolder>& classMap = value;
		member.presets.classMap = classMap;
		return true;
	}
	if (text == "functions-registed")
	{
		const BMap<BString, FunctionHolder>& funcMap = value;
		member.presets.functionMap = funcMap;
		return true;
	}
	return BObject::input(text, value);
}
bool BScript::query(const BString& text, BValue& value) const
{
	if (text == "stack")
	{
		BVariantArray* stack = value.fill<BVariantArray>();
		stack->reset();
		stack->append(member.stack.data(), member.stack.size());
		return true;
	}
	if (text == "sequence")
	{
		BExpHolderArray& sequence = value.fill<BExpHolderArray>();
		sequence = member.sequence;
		return true;
	}
	if (text == "classes")
	{
		BMap<BString, BClassHolder>& classMap = value.fill<BMap<BString, BClassHolder>>();
		classMap = member.classMap;
		return true;
	}
	if (text == "functions")
	{
		BStringArray& funcNames = value.fill<BStringArray>();
		funcNames.reset();
		for (auto it = member.functionMap.begin(); it.valid(); ++it)
		{
			BString& name = funcNames.append();
			name << (*it)->type << ' ' << it.key();
		}
		return true;
	}
	if (text == "classes-registed")
	{
		BMap<BString, BClassHolder>& classMap = value.fill<BMap<BString, BClassHolder>>();
		classMap = member.presets.classMap;
		return true;
	}
	if (text == "functions-registed")
	{
		BMap<BString, FunctionHolder>& funcMap = value.fill<BMap<BString, FunctionHolder>>();
		funcMap = member.presets.functionMap;
		return true;
	}
	return BObject::query(text, value);
}
